/** EN7894 Concurrent Programming Assign #1 Question 2b
 * 
 * Submitted By: 	Robert Lockyer (200534006)
 * 					Andrew Carter (200517548)
 * 
 * Date Due: May 28th, 2010
 */

package assign1;

import assign1.RecSummer;
import assign1.RecSummer.RecSumWorker;

public class RecSummer {

	int totalSum = 0;
	int numElementsInArray = 50;
	int PR = 25;
	int indicieWidth = numElementsInArray/PR;
	
	int[] arrayToSum = new int[numElementsInArray];
	
	private void initializeArray()
	{
		for(int i=0; i < numElementsInArray; ++i)
		{
			arrayToSum[i] = (int)(Math.random() * 1000);
		}
	}
	
	public void calculateSum()
	{
		/* Using the Concurrent Composition Rule:
		 * {totalSum = 0 ^ numElementsInArray=X ^ PR=Y ^ X/Y=E{NaturalNumbers} ^ arrayToSum=Z}
		 */
		RecSumWorker mainWorker = new RecSumWorker(numElementsInArray, 0);
		// co
		mainWorker.start();
		
		while(!mainWorker.getIsDone())
		{
			try {
				Thread.sleep(42);
			} catch (InterruptedException e) {}
		}
		// oc
		
		
		// {mainWorker.getSum() = sigma(i=0; X-1; Z[i])}}
		totalSum = mainWorker.getSum();
		
		// Postconditions
		// {totalSum = sigma(i=0; X-1; Z[i])}
	}
	
	
	public class RecSumWorker extends Thread
	{
		int _numElementsToSum = 0;
		int _firstIndicie = 0;
		boolean _isDone = false;
		int _sum = 0;
		
		RecSumWorker(int numElementsToSum, int firstIndicie)
		{
			_numElementsToSum = numElementsToSum;
			_firstIndicie = firstIndicie;
		}
		
		@Override
		public void run()
		{
			doSum();
		}
		
		public boolean getIsDone()
		{
			return _isDone;
		}
		
		public int getSum()
		{
			return _sum;
		}

		private void doSum()
		{
			// base case
			if(_numElementsToSum <= indicieWidth)
			{
				// {_sum =0 ^ _numElementsToSum = j ^ _firstIndice = k}
				for(int i = _firstIndicie; i < (_numElementsToSum + _firstIndicie); ++i) // LI: sum = sigma(y=k; i-1; Z[y])
				{
					_sum += arrayToSum[i]; // sum = sigma(y=k; i; Z[y])
				}
				// {sum = sigma(y=k; i-1; Z[y]) ^ i = (k+j)}
				_isDone = true;
			}
			else // recursive case
			{
				RecSumWorker lowWorker = new RecSumWorker(_numElementsToSum/2, _firstIndicie);
				RecSumWorker highWorker = new RecSumWorker((_numElementsToSum-_numElementsToSum/2), 
						(_firstIndicie + (_numElementsToSum/2)));
				
				lowWorker.start(); // // (spawn new parallel execution)
				highWorker.start();
				
				while(!lowWorker.getIsDone() || !highWorker.getIsDone())
				{
					try {
						Thread.sleep(42);
					} catch (InterruptedException e) {}
				}
				
				// {lowWorker.getSum() = sigma(j=_firstIndicie; j< (_numElementsToSum/2 + _firstIndicie); Z[j]) ^ 
				//  highWorker.getSum() = sigma(j=_firstIndicie+(_numElementsToSum/2); (_numElementsToSum-_numElementsToSum/2)+ _firstIndicie; Z[j])}
				_sum = lowWorker.getSum() + highWorker.getSum();
				_isDone = true;
			}
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		RecSummer recSummer = new RecSummer();
		recSummer.initializeArray();
		
		int sequentialSum = 0;
		// print values of array
		for (Integer intToPrint : recSummer.arrayToSum) {
			System.out.println(intToPrint.toString());
			sequentialSum += intToPrint;
		}
		
		System.out.println("Sequential Sum = " + sequentialSum);
		
		recSummer.calculateSum();
		System.out.println("Concurrent Sum = " + recSummer.totalSum);
	}
}
